#!/usr/bin/env python
#-*- coding:utf-8 -*-
import os,sys,re
from wwpy.util.Logger import logger
from wwpy.math.Num import Num
from wwpy.io.Source import Source

class Reader(object):
    
    def __init__(self):
        self.is_group=False
    
    def read(self,source=None):
        src=Source(source)
        self.files=src.sources
        self.lines=src.read()
        return self
    
    def _cat(self,lines):
        for line in lines:
            yield line
    
    def cat(self):
        self.lines=self._cat(self.lines)
        return self
    
    def _grep(self,lines,pattern):
        regex=re.compile(pattern)
        for line in lines:
            if regex.search(line):
                yield line
            
    def grep(self,pattern):
        self.lines=self._grep(self.lines,pattern)
        return self
    
    def _map(self,lines,func):
        for line in lines:
            yield func(line)
            
    def map(self,func):
        self.lines=self._map(self.lines,func)
        return self
    
    def _filter(self,lines,func):
        for line in lines:
            if func(line):
                yield line
                
    def filter(self,func):
        self.lines=self._filter(self.lines,func)
        return self
    
    def wc_l(self):
        return sum(1 for line in self.lines)
    
    def _sub(self,lines,old,new):
        regex=re.compile(old)
        for line in lines:
            line=regex.sub(new,line)
            yield line
            
    def sub(self,old,new):
        self.lines=self._sub(self.lines,old,new)
        return self
    
    def _handle(self,lines,transform):
        for line in lines:
            yield transform(line)
        
    def handle(self,transform=lambda line:line.replace('\n','')):
        self.lines=self._handle(self.lines,transform)
        return self
    
    def get(self):
        return self.lines
    
    def set(self,obj):
        self.lines=obj.lines
    
    def _more2(self,lines,num):
        lines=[line for line in lines]
        while lines:
            chuck=lines[:num]
            lines=lines[num:]
            for line in chuck:
                yield line
            if len(lines) and raw_input()!='':
                break
            
    def _more(self,lines,num):
        chuck=[]
        for idx,line in enumerate(lines):
            if idx<num or raw_input()=='':
                yield line
            else:
                break
            
    def more(self,num=15):
        self.lines=self._more(self.lines,num)
        return self
    
    def _range(self,lines,start,end,step):
        count=-step
        start=0 if start<0 else start
        end=start if end is None or end<start else end
        for line in lines:
            count+=step
            if count>=start and count<end:
                yield line
            elif count>=end:
                break
            
    def range(self,start=0,end=None,step=1):
        self.lines=self._range(self.lines,start,end,step)
        return self
    
    def _row(self,lines,index):
        index=1 if index<=0 else index
        count=0
        for line in lines:
            count+=1
            if count==index:
                yield line
            elif count>index:
                break
    
    def row(self,index):
        self.lines=self._row(self.lines,index)
        return self
    
    def _top(self,lines,num):
        num=1 if num<=0 else num
        count=0
        for line in lines:
            count+=1
            if count<=num:
                yield line
            elif count>num:
                break
            
    def top(self,num=10):
        self.lines=self._top(self.lines,num)
        return self
    
    def _skip(self,lines,num):
        num=0 if num<0 else num
        count=0
        for line in lines:
            count+=1
            if count<num:
                continue
            else:
                yield line
                
    def skip(self,num=0):
        self.lines=self._skip(self.lines,num)
        return self
    
    def _head(self,lines,num):
        count=-1
        for line in lines:
            count+=1
            if count<num:
                yield line
            else:
                break
            
    def head(self,num=10):
        self.lines=self._head(self.lines,num)
        return self
    
    def _tail(self,lines,num):
        lines=[line for line in lines]
        line_num=len(lines)
        if line_num<=num:
            for line in lines:
                yield line
        else:
            for line in lines[line_num-num:]:
                yield line
                
    def tail(self,num=10):
        self.lines=self._tail(self.lines,num)
        return self
    
    def _split(self,lines,sep):
        for line in lines:
            fields=line.strip().split(sep)
            yield fields
            
    def split(self,sep='\t'):
        self.lines=self._split(self.lines,sep)
        return self
    
    def _sort(self,lines,key,reverse):
        return sorted(lines,key=key,reverse=reverse)
    
    def sort(self,key=lambda f:f[0],reverse=True):
        self.lines=self._sort(self.lines,key,reverse)
        return self
    
    def _fields(self,fields):
        if fields is not None and not isinstance(fields,(tuple,list)):
            fields=[fields]
        return fields
    
    def _select(self,lines,cols):
        for line in lines:
            fields=map(lambda field:line[field],cols)
            yield fields
            
    def select(self,fields=None):
        fields=self._fields(fields)
        self.lines=self._select(self.lines,fields)
        return self
    
    def _join(self,lines,sep):
        for fields in lines:
            yield sep.join(str(field) for field in fields)
            
    def join(self,sep='\t'):
        self.lines=self._join(self.lines,sep)
        return self
    
    def _group(self,lines,fields):
        for line in lines:
            keys=map(lambda col:line[col],fields)
            yield line,tuple(keys)
            
    def group(self,keys):
        self.lines=self._group(self.lines,keys)
        self.is_group=True
        return self
    
    def _uniq(self,lines):
        info={}
        for line in lines:
            if self.is_group:
                [line],key=line[:-1],line[-1]
            else:
                key=tuple(line)
            if key not in info:
                info[key]=1
                yield key
                
    def uniq(self):
        self.lines=self._uniq(self.lines)
        return self
    
    def _value(self,line,value):
        try:
            value=line[value]
        except (IndexError,TypeError):
            value=None
        return value
    
    def _count(self,lines,values):
        num=len(values)
        if self.is_group:
            info={}
            for line in lines:
                [line],key=line[:-1],line[-1]
                for idx,value in enumerate(values):
                    value=self._value(line,value)
                    info.setdefault(key,[0 for i in range(num)])
                    info[key][idx]+=1 if value is not None else 0
            for key in info:
                yield list(key if isinstance(key,tuple) else [key])+info[key]
        else:
            info=[0 for i in range(num)]
            for line in lines:
                for idx,value in enumerate(values):
                    value=self._value(line,value)
                    info[idx]+=1 if value is not None else 0
            yield info
            
    def count(self,values):
        values=self._fields(values)
        self.lines=self._count(self.lines,values)
        return self
    
    def _sum(self,lines,values):
        num=len(values)
        if self.is_group:
            info={}
            for line in lines:
                [line],key=line[:-1],line[-1]
                for idx,value in enumerate(values):
                    value=self._value(line,value)
                    info.setdefault(key,[0 for i in range(num)])
                    value=Num(value).get()
                    info[key][idx]+=value if value is not None else 0
            for key in info:
                yield list(key if isinstance(key,tuple) else [key])+info[key]
        else:
            info=[0 for i in range(num)]
            for line in lines:
                for idx,value in enumerate(values):
                    value=self._value(line,value)
                    value=Num(value).get()
                    info[idx]+=value if value is not None else 0
            yield info
            
    def __iter__(self):
        return iter(self.lines)
    
    def scan(self):
        for line in self.lines:
            print line
    
    def sum(self,values):
        values=self._fields(values)
        self.lines=self._sum(self.lines,values)
        return self
    
    def copy(self,from_,to_):
        logger.info("Copy from %s to %s" % (from_,to_))
        from wwpy.io.Writer import writer
        writer.info(self.read(from_).lines)
        writer.output(to_)
        writer.write()
        
    def write(self,output,**kwargs):
        from wwpy.io.Writer import writer
        writer.output(output)
        writer.info(self.lines)
        writer.write()
    
reader=Reader()
    
